function varargout = verifyModel2Reqs(modelName, top, varargin)
%VERIFYMODEL2REQS 验证模型对需求的符合性
%   此函数验证Simulink模型是否符合高级软件需求，并执行模型覆盖率分析。
%   所有测试通过主机上的标准仿真来验证编译后的模型。
%
%语法:
%   verifyModel2Reqs(modelName)
%   verifyModel2Reqs(modelName, 'TreatAsTopMdl')
%   verifyModel2Reqs(modelName, 'TreatAsTopMdl', authorNames)
%   verifyModel2Reqs(modelName, 'TreatAsTopMdl', authorNames, 'CI')
%   verifyModel2Reqs(modelName, 'TreatAsTopMdl', [], 'CI')
%   verifyModel2Reqs(modelName, [], authorNames, 'CI')
%   verifyModel2Reqs(modelName, [], [], 'CI')
%   result = verifyModel2Reqs(modelName, ...)
%
%输入参数:
%   modelName - Simulink模型名称（字符串）
%   top - 模型处理方式：
%         'TreatAsTopMdl' - 作为顶层模型处理
%         [] 或其他 - 作为引用模型处理
%
%可选参数:
%   authorNames - 作者姓名列表（字符串或元胞数组）
%   'CI' - 持续集成环境模式
%   'DEV' - 开发环境模式（默认）
%
%输出参数:
%   result - 结构体（可选），包含验证结果信息
%            .Message - 验证结果消息
%            .TestResults - 测试执行结果
%            .CoverageResults - 覆盖率分析结果
%
%说明:
%   - 检查Simulink Test和Simulink Coverage许可证
%   - 关闭所有模型并清理覆盖率数据
%   - 删除旧的测试结果和报告文件
%   - 执行高级需求仿真测试
%   - 进行模型覆盖率分析
%   - 生成详细的验证报告
%
%验证流程:
%   1. 环境准备和许可证检查
%   2. 清理旧的测试数据和报告
%   3. 加载模型和测试用例
%   4. 执行需求验证测试
%   5. 收集和分析覆盖率数据
%   6. 生成综合验证报告
%
%测试类型:
%   - 高级需求验证测试（HLR Tests）
%   - 模型仿真覆盖率分析
%   - 需求追踪验证
%
%输出文件:
%   - 测试结果文件：[ModelName]_HLR_SimResults.mldatx
%   - 测试报告：[ModelName]_HLR_SimReport.pdf
%   - 覆盖率文件：[ModelName]_HLR_ModelCoverage.cvt
%
%环境模式:
%   DEV模式：开发环境，显示详细信息和报告
%   CI模式：持续集成环境，静默执行
%
%先决条件:
%   - 需要Simulink Test许可证
%   - 需要Simulink Coverage许可证
%   - 模型必须有对应的测试用例
%   - 项目结构必须正确配置
%
%示例:
%   % 基本需求验证
%   verifyModel2Reqs('MyModel');
%
%   % 顶层模型验证，指定作者
%   result = verifyModel2Reqs('MyModel', 'TreatAsTopMdl', 'John Doe');
%   fprintf('验证结果: %s\n', result.Message);
%
%   % CI环境中的批量验证
%   models = getModelList();
%   for i = 1:length(models)
%       try
%           verifyModel2Reqs(models{i}, [], [], 'CI');
%           fprintf('✓ %s 需求验证完成\n', models{i});
%       catch ME
%           fprintf('✗ %s 需求验证失败: %s\n', models{i}, ME.message);
%       end
%   end
%
%覆盖率指标:
%   - 决策覆盖率（Decision Coverage）
%   - 条件覆盖率（Condition Coverage）
%   - 修改条件/决策覆盖率（MCDC）
%   - 语句覆盖率（Statement Coverage）
%
%依赖项:
%   dig.isProductInstalled - 检查产品安装状态
%   bdclose - 关闭Simulink模型
%   cvexit - 清理覆盖率数据
%   ProjectPathManager.getDirPath - 获取项目目录路径
%   ProjectNamingManager.getNameStr - 获取标准化名称
%
%参见:
%   sltest.testmanager, slcov, Simulink.Test, Simulink.Coverage
%

%版本: 1.0
%日期: 2021-2024
%


if ~dig.isProductInstalled('Simulink Test')
    error('A Simulink Test license is not available.');
end
if ~dig.isProductInstalled('Simulink Coverage')
    error('A Simulink Coverage license is not available.');
end

% Close all models.
bdclose('all');

% Clear all coverage data.
cvexit();

% Administer options.
if nargin > 1 && ~isempty(top)
    isTop = true;
else
    isTop = false;
end

% Delete the old results and reports if they exist.
resultFile = fullfile(ProjectPathManager.getDirPath('HLR sim results', modelName), [ProjectNamingManager.getNameStr('HLR sim results', modelName), '.mldatx']);
if exist(resultFile, 'file')
    delete(resultFile);
end
% 基于规则第3条：使用getRptPath()从配置文件读取报告格式
rptFile = getRptPath('HLR sim report', modelName);
if exist(rptFile, 'file')
    delete(rptFile);
end
docDir = fullfile(ProjectPathManager.getDirPath('HLR sim results', modelName), ProjectNamingManager.getNameStr('HLR sim report', modelName));
if exist(docDir, 'dir')
    rmdir(docDir, 's');
end
cvtFile = fullfile(ProjectPathManager.getDirPath('HLR model coverage', modelName), [ProjectNamingManager.getNameStr('HLR model coverage', modelName), '.cvt']);
if exist(cvtFile, 'file')
    delete(cvtFile);
end
htmlFile = fullfile(ProjectPathManager.getDirPath('HLR model coverage', modelName), [ProjectNamingManager.getNameStr('HLR model coverage report', modelName), '.html']);
if exist(htmlFile, 'file')
    delete(htmlFile);
end
gifDir = fullfile(ProjectPathManager.getDirPath('HLR model coverage', modelName), 'scv_images');
if exist(gifDir, 'dir')
    rmdir(gifDir, 's');
end

% Check for prerequisites.
RBTTestFile = fullfile(ProjectPathManager.getDirPath('HLR test cases', modelName), [ProjectNamingManager.getNameStr('HLR test cases', modelName), '.mldatx']);
if ~exist(RBTTestFile, 'file')
    error(['Test file ''', RBTTestFile, ''' not found.']);
end

% Get model information.
% If any of the test case in the test file needs to perform a load_system
% on the test harness, querying the checksum after loading the test file
% leads to an error. To avoid the potential error, get the checksum
% information before loading the test file.
load_system(modelName);
modelVersion = get_param(modelName, 'ModelVersion');
modifiedDate = get_param(modelName, 'LastModifiedDate');
% if isTop
%     modelChecksum = getModelChecksum(modelName, 'TreatAsTopMdl');
% else
%     modelChecksum = getModelChecksum(modelName);
% end

% Verify the model against HLR test cases in the test file.
disp(['Running tests on Simulink model ', modelName, '.']);
sltest.testmanager.clear();
sltest.testmanager.clearResults();
testFile = sltest.testmanager.load(RBTTestFile);
testResult = testFile.run();

% Attach model checksum information to test results.
checksumStr = sprintf(['Model Version: ', modelVersion, '\n\n', ...
    'Model Last Modified On: ', datestr(modifiedDate(5:end), 'dd-mmm-yyyy HH:MM:SS')]);
% if isTop
%     checksumStr = sprintf(['Model Version: ', modelVersion, '\n\n', ...
%         'Model Last Modified On: ', datestr(modifiedDate(5:end), 'dd-mmm-yyyy HH:MM:SS'), '\n\n', ...
%         'Checksum when Compiled as Top Model: ', num2str(modelChecksum')]);
% else
%     checksumStr = sprintf(['Model Version: ', modelVersion, '\n\n', ...
%         'Model Last Modified On: ', datestr(modifiedDate(5:end), 'dd-mmm-yyyy HH:MM:SS'), '\n\n', ...
%         'Checksum when Compiled as Referenced Model: ', num2str(modelChecksum')]);
% end
testResult.getTestFileResults.Description = checksumStr;

% Save test results.
sltest.testmanager.exportResults(testResult, resultFile);

% Save coverage results.
if ~isempty(testResult.CoverageResults)
    cvsave(cvtFile, cv.cvdatagroup(testResult.CoverageResults));
end

if nargin > 2 && ~isempty(varargin{1})
    authors = varargin{1};
else
    authors = '';
end

result.Message = ['Requirement-based simulation test report for ', modelName, ' is successfully generated.'];
if nargin > 3 && ~isempty(varargin{2})
    LaunchReport = false;
    cvhtmlOption = '-sRT=0';
    result.Method = 'verifyModel2Reqs';
    result.Component = modelName;
    result.NumTotal = testResult.getTestFileResults().NumTotal;
    result.NumPass = testResult.getTestFileResults().NumPassed;
    result.NumWarn = testResult.getTestFileResults().NumIncomplete;
    result.NumFail = testResult.getTestFileResults().NumFailed;
    if result.NumFail > 0
        result.Outcome = -1;
    elseif result.NumWarn > 0
        result.Outcome = 0;
    else
        result.Outcome = 1;
    end
    if ~isempty(testResult.CoverageResults)
        cov = cv.cvdatagroup(testResult.CoverageResults);
        result.ExecutionCov = executioninfo(cov, modelName);
        result.DecisionCov = decisioninfo(cov, modelName);
        result.ConditionCov = conditioninfo(cov, modelName);
        result.MCDCCov = mcdcinfo(cov, modelName);
    end
    result.Results = testResult.getTestFileResults();
    varargout{1} = result;
else
    LaunchReport = true;
    cvhtmlOption = '-sRT=1';
    if nargout > 0
        varargout{1} = result;
    end
end

% Generate the test report.
sltest.testmanager.report(testResult, rptFile, ...
    'Author', authors, ...
    'Title',[modelName, ' REQ-Based Tests'], ...
    'IncludeMLVersion', true, ...
    'IncludeTestRequirement', true, ...
    'IncludeSimulationSignalPlots', true, ...
    'IncludeComparisonSignalPlots', false, ...
    'IncludeErrorMessages', true, ...
    'IncludeTestResults', 0, ...
    'IncludeCoverageResult', true, ...
    'IncludeSimulationMetadata', true, ...
    'LaunchReport', LaunchReport);

% Generate the coverage report.
if ~isempty(testResult.CoverageResults)
    cvhtml(htmlFile, cv.cvdatagroup(testResult.CoverageResults), cvhtmlOption);
end

disp(result.Message);

end
